﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.Assertions;

namespace Nirvana
{
    /// <summary>
    /// 实体引用计数缓存脚本，记录对应的prefab创建的所有实体对象
    /// </summary>
    public sealed class GameObjectCache
    {
        /// <summary>
        /// 对象池缓存
        /// </summary>
        public struct PoolCacheItem
        {
            public GameObject CacheObject;//在池中空闲的实例对象
            public float LastFreeTime;//最后放入缓存池时间
        }

        /// <summary>
        /// prefab资源
        /// </summary>
        private GameObject instancePrefab;
        private PoolStrategy poolstrategy;
        /// <summary>
        /// 记录被引用激活的缓存：不在对象池中的
        /// </summary>
        private Dictionary<int, GameObjectCache> mActiveCacheMap;
        /// <summary>
        /// 当前对象池中缓存的实体对象列表
        /// </summary>
        public List<PoolCacheItem> PoolCacheList = new List<PoolCacheItem>();

        public float ReleaseAfterFree { get; set; }
        public string Error { get; private set; }
        private bool mLoading;
        public bool Loading {
            get { return mLoading; }
            set {
                mLoading = value;
            }
        }

        /// <summary>
        /// 引用计数
        /// </summary>
        public int SpawnCount { get; set; }
        /// <summary>
        /// 对象池中空闲缓存数量
        /// </summary>
        public int CacheCount
        {
            get
            {
                return this.PoolCacheList.Count;
            }
        }
        /// <summary>
        /// 缓存次能够缓存的数量，超过了就抛弃，避免占用过多内存
        /// </summary>
        public int maxPoolCacheCount { get; set; }
        private float defaultReleaseAfterFree = 30f;
        private int defaultMaxPoolCount = 16;
        //限制创建数量，缓存池中没有空闲对象就不创建新对象
        private bool isLimitSpwan = false;

        public AssetID assetid;
        private bool isLoaded;
      	private readonly Predicate<PoolCacheItem> _freeJudge;
        private sealed class GameObjectCahceEvent
        {
            public Action<GameObject> callback;
            public GameObjectCache cache;

            public void Init(GameObject obj)
            {
                cache.mActiveCacheMap.Add(obj.GetInstanceID(), cache);
                callback(obj);
            }
        }

        public GameObjectCache(Dictionary<int, GameObjectCache> map)
        {
            this.mActiveCacheMap = map;
            _freeJudge = IsFree;
        }

        public bool IsLimitSpwan()
        {
            return isLimitSpwan;
        }
        public bool IsNotNull()
        {
            return this.instancePrefab != null;
        }
        public GameObject GetPrefab()
        {
            return this.instancePrefab;
        }
        public void SetLimitSpwan(bool t)
        {
            if (poolstrategy != null)
                this.isLimitSpwan = poolstrategy.IsLimitSpwan;
            else
                this.isLimitSpwan = t;
        }
        public void SetReleaseTimeAfterFree(float t)
        {
            this.defaultReleaseAfterFree = t;
            if (poolstrategy != null)
                this.ReleaseAfterFree = poolstrategy.InstanceReleaseAfterFree;
            else
                this.ReleaseAfterFree = t;
        } 

        public void SetPoolCount(int count)
        {
            this.defaultMaxPoolCount = count;
            if (poolstrategy != null)
                this.maxPoolCacheCount = poolstrategy.InstancePoolCount;
            else
                this.maxPoolCacheCount = count;
        }

        /// <summary>
        /// 设置当前prefab对象池策略
        /// </summary>
        /// <param name="prefab"></param>
        public void SetPoolStrategyData(GameObject prefab)
        {
            this.instancePrefab = prefab;
            this.poolstrategy = this.instancePrefab.GetComponent<PoolStrategy>();
            if (this.poolstrategy != null)
            {
                this.ReleaseAfterFree = this.poolstrategy.InstanceReleaseAfterFree;
                this.maxPoolCacheCount = this.poolstrategy.InstancePoolCount;
                this.isLimitSpwan = this.poolstrategy.IsLimitSpwan;
            }
            else
            {
                this.ReleaseAfterFree = defaultReleaseAfterFree;
                this.maxPoolCacheCount = defaultMaxPoolCount;
                this.isLimitSpwan = false;
            }
        }

        /// <summary>
        /// 启动加载对象
        /// </summary>
        /// <param name="assetid"></param>
        public void SpwanAsset(AssetID assetid)
        {
            this.assetid = assetid;
            this.Loading = true;
            PrefabPool.Instance.Load(assetid, (prefab) => {
                if (prefab == null)
                {
                    this.Error = string.Format("This asset: {0} is not a GameObject", this.assetid);
                    return;
                }
                
                SetPoolStrategyData(prefab);
                isLoaded = true;

            }, false);
        }

        /// <summary>
        /// 清除Prefab资源：当引用计数为0，对象池为空时
        /// </summary>
        public void Unload()
        {
            if (this.instancePrefab != null)
            {
                if (isLoaded)
                {
                    PrefabPool.Instance.Free(this.instancePrefab, false);
                }
                this.instancePrefab = null;
            }
        }

        /// <summary>
        /// 对象池中获取，没有就创建
        /// </summary>
        public GameObject PoolGet(Transform parent = null)
        {        

            //检测缓冲池中是否有空闲实体对象
            while (this.PoolCacheList.Count > 0)
            {
                int lastindex = this.PoolCacheList.Count - 1;
                PoolCacheItem cacheItem = this.PoolCacheList[lastindex];
                this.PoolCacheList.RemoveAt(lastindex);
                GameObject cacheObject = cacheItem.CacheObject;
                if (cacheObject != null)
                {
                    this.SpawnCount++;

                    if (!cacheObject.activeSelf) cacheObject.SetActive(true);
                    if (parent != null) cacheObject.transform.SetParent(parent);
                    cacheObject.transform.localPosition = this.instancePrefab.transform.localPosition;
                    cacheObject.transform.localRotation = this.instancePrefab.transform.localRotation;
                    cacheObject.transform.localScale = this.instancePrefab.transform.localScale;
                    this.mActiveCacheMap.Add(cacheObject.GetInstanceID(), this);
                    return cacheObject;
                }
                else
                    Debug.LogWarningFormat("The instance of prefab '{0}'in the pool has been destroy.", this.instancePrefab.name);
            }
            if (isLimitSpwan && this.SpawnCount >= this.maxPoolCacheCount)
            {
                return null;
            }

            //对象池中没有缓存则立即创建
            this.SpawnCount++;
            GameObject gameObject = PrefabPool.Instance.Instantiate(this.instancePrefab);
            if (parent != null) gameObject.transform.SetParent(parent);
            gameObject.transform.localPosition = Vector3.zero;
            gameObject.transform.localScale = Vector3.one;
            this.mActiveCacheMap.Add(gameObject.GetInstanceID(), this);
            return gameObject;

        }
     
        public void LoadObject(InstantiateQueue instqueue,int index, Action<GameObject> cb)
        {
            GameObjectCahceEvent _event = new GameObjectCahceEvent();
            _event.callback = cb;
            _event.cache = this;
            this.SpawnCount++;
            if(this.PoolCacheList.Count > 0)
            {
                int lastindex = this.PoolCacheList.Count - 1;
                var cacheItem = this.PoolCacheList[lastindex];
                this.PoolCacheList.RemoveAt(lastindex);
                GameObject cacheObject = cacheItem.CacheObject;
                Assert.IsNotNull<GameObject>(cacheObject);
                if (cacheObject != null)
                {
                    cacheObject.SetActive(true);
                    cacheObject.transform.localPosition = this.instancePrefab.transform.localPosition;
                    cacheObject.transform.localRotation = this.instancePrefab.transform.localRotation;
                    cacheObject.transform.localScale = this.instancePrefab.transform.localScale;
                    this.mActiveCacheMap.Add(cacheObject.GetInstanceID(), this);
                    _event.callback(cacheObject);
                }
            }else
            {
                instqueue.Instantiate(this.instancePrefab, index, _event.callback);
            }
        }
        /// <summary>
        /// 放回缓存池
        /// </summary>
        /// <param name="go"></param>
		        /// <param name="isNoSave">是否放入缓存池</param>
        public void PoolCache(GameObject go, bool isNoSave)
        {
            this.Free();

            //不放回缓存池，或者数量已超，则抛弃
            if (isNoSave || this.PoolCacheList.Count > this.maxPoolCacheCount)
            {
                UnityEngine.Object.Destroy(go);
                return;
            }
            PoolCacheItem item = default(PoolCacheItem);
            item.CacheObject = go;
            item.LastFreeTime = Time.realtimeSinceStartup;
            this.PoolCacheList.Add(item);
        }

        /// <summary>
        /// 引用计数-1
        /// </summary>
        public void Free()
        {
            this.SpawnCount--;
            if (this.SpawnCount < 0)
            {
                string txt = (!this.instancePrefab) ? "undefined" : this.instancePrefab.name;
                Debug.LogErrorFormat("[GameObjecCahce] spawnCount is error {0} {1}", txt, this.SpawnCount);
            }
        }

        /// <summary>
        /// 清除对象池中超时未使用的实体对象，避免一直占用内存
        /// </summary>
        public void ClearOutTime()
        {
            PoolCacheList.RemoveAll(_freeJudge);
        }


        private bool IsFree(PoolCacheItem item)
        {
            if (item.CacheObject == null)
            {
                Debug.LogFormat("The instance of prefab '{0}'in the pool has been destroy.", this.instancePrefab.name);
                return true;
            }

            float curTime = item.LastFreeTime + ReleaseAfterFree;
            if (Time.realtimeSinceStartup > curTime)
            {
                UnityEngine.GameObject.Destroy(item.CacheObject);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 清空对象池
        /// </summary>
        public void Clear()
        {
            foreach (PoolCacheItem cacheItem in this.PoolCacheList)
            {
                UnityEngine.Object.Destroy(cacheItem.CacheObject);
            }
            this.PoolCacheList.Clear();
        }

    }
}

